<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebRTC视频通话演示 - v2.0</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            margin-bottom: 20px;
        }
        .status {
            padding: 10px;
            border-radius: 4px;
            margin: 10px 0;
        }
        .status.connected {
            background-color: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        .status.disconnected {
            background-color: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        .status.connecting {
            background-color: #fff3cd;
            color: #856404;
            border: 1px solid #ffeaa7;
        }
        .controls {
            display: flex;
            gap: 10px;
            margin: 20px 0;
            flex-wrap: wrap;
        }
        button {
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.3s;
        }
        button.primary {
            background-color: #007bff;
            color: white;
        }
        button.primary:hover {
            background-color: #0056b3;
        }
        button.success {
            background-color: #28a745;
            color: white;
        }
        button.success:hover {
            background-color: #1e7e34;
        }
        button.danger {
            background-color: #dc3545;
            color: white;
        }
        button.danger:hover {
            background-color: #c82333;
        }
        button:disabled {
            background-color: #6c757d;
            cursor: not-allowed;
        }
        .message-area {
            display: flex;
            gap: 20px;
            margin: 20px 0;
        }
        .message-input {
            flex: 1;
        }
        .message-input input {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 14px;
        }
        .message-log {
            background-color: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 4px;
            padding: 15px;
            height: 200px;
            overflow-y: auto;
            font-family: monospace;
            font-size: 12px;
            line-height: 1.4;
        }
        .video-container {
            display: flex;
            gap: 20px;
            margin: 20px 0;
        }
        .video-box {
            flex: 1;
            text-align: center;
        }
        video {
            width: 100%;
            max-width: 400px;
            height: 300px;
            background-color: #000;
            border-radius: 4px;
        }
        .log-entry {
            margin: 2px 0;
            padding: 2px 5px;
            border-radius: 2px;
        }
        .log-entry.info {
            background-color: #e3f2fd;
        }
        .log-entry.success {
            background-color: #e8f5e8;
        }
        .log-entry.error {
            background-color: #ffebee;
        }
        .log-entry.warning {
            background-color: #fff3e0;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>WebRTC P2P Demo</h1>
        
        <!-- 连接状态 -->
        <div id="connectionStatus" class="status disconnected">
            状态: 未连接
        </div>
        
        <!-- 控制按钮 -->
        <div class="controls">
            <button id="connectBtn" class="primary">连接信令服务器</button>
            <button id="startCallBtn" class="success" disabled>开始通话</button>
            <button id="endCallBtn" class="danger" disabled>结束通话</button>
            <button id="refreshServersBtn" class="primary">刷新服务器列表</button>
        </div>
        
        <!-- 消息发送区域 -->
        <div class="message-area">
            <div class="message-input">
                <input type="text" id="messageInput" placeholder="输入要发送的消息..." disabled>
            </div>
            <button id="sendMessageBtn" class="primary" disabled>发送消息</button>
        </div>
        
        <!-- 消息日志 -->
        <div class="container">
            <h3>消息日志</h3>
            <div id="messageLog" class="message-log"></div>
        </div>
        
        <!-- 视频区域 -->
        <div class="video-container">
            <div class="video-box">
                <h3>远程视频流</h3>
                <video id="remoteVideo" autoplay playsinline muted></video>
            </div>
        </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.js"></script>
    <script>
        class WebRTCClient {
            constructor() {
                this.socket = null;
                this.peerConnection = null;
                this.dataChannel = null;
                this.clientId = null;
                this.isConnected = false;
                this.isCallActive = false;
                this.reconnectAttempts = 0;
                this.maxReconnectAttempts = 5;
                this.reconnectDelay = 2000;
                
                this.initializeElements();
                this.setupEventListeners();
            }
            
            initializeElements() {
                this.elements = {
                    connectionStatus: document.getElementById('connectionStatus'),
                    connectBtn: document.getElementById('connectBtn'),
                    startCallBtn: document.getElementById('startCallBtn'),
                    endCallBtn: document.getElementById('endCallBtn'),
                    refreshServersBtn: document.getElementById('refreshServersBtn'),
                    messageInput: document.getElementById('messageInput'),
                    sendMessageBtn: document.getElementById('sendMessageBtn'),
                    messageLog: document.getElementById('messageLog'),
                    remoteVideo: document.getElementById('remoteVideo')
                };
            }
            
            setupEventListeners() {
                this.elements.connectBtn.addEventListener('click', () => this.connect());
                this.elements.startCallBtn.addEventListener('click', () => this.startCall());
                this.elements.endCallBtn.addEventListener('click', () => this.endCall());
                this.elements.refreshServersBtn.addEventListener('click', () => this.refreshServers());
                this.elements.sendMessageBtn.addEventListener('click', () => this.sendMessage());
                
                this.elements.messageInput.addEventListener('keypress', (e) => {
                    if (e.key === 'Enter') {
                        this.sendMessage();
                    }
                });
            }
            
            connect() {
                if (this.socket && this.socket.connected) {
                    this.log('已经连接到信令服务器', 'warning');
                    return;
                }
                
                this.updateStatus('connecting', '正在连接...');
                this.log('正在连接到信令服务器...', 'info');
                
                this.socket = io();
                
                this.socket.on('connect', () => {
                    this.isConnected = true;
                    this.reconnectAttempts = 0;
                    this.updateStatus('connected', '已连接到信令服务器');
                    this.log('成功连接到信令服务器', 'success');
                    this.updateButtonStates();
                });
                
                this.socket.on('disconnect', () => {
                    this.isConnected = false;
                    this.updateStatus('disconnected', '与信令服务器断开连接');
                    this.log('与信令服务器断开连接', 'error');
                    this.updateButtonStates();
                    this.attemptReconnect();
                });
                
                this.socket.on('connected', (data) => {
                    this.clientId = data.client_id;
                    this.log(`客户端ID: ${this.clientId}`, 'info');
                });
                
                this.socket.on('webrtc_signal', (data) => {
                    this.handleSignalingMessage(data);
                });
                
                this.socket.on('servers_list', (data) => {
                    this.log(`可用服务器: ${data.servers.length}个`, 'info');
                });
                
                this.socket.on('error', (data) => {
                    this.log(`错误: ${data.message}`, 'error');
                });
            }
            
            attemptReconnect() {
                if (this.reconnectAttempts < this.maxReconnectAttempts) {
                    this.reconnectAttempts++;
                    this.log(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`, 'warning');
                    
                    setTimeout(() => {
                        this.connect();
                    }, this.reconnectDelay * this.reconnectAttempts);
                } else {
                    this.log('重连失败，请手动重新连接', 'error');
                }
            }
            
            async startCall() {
                if (!this.isConnected) {
                    this.log('请先连接到信令服务器', 'error');
                    return;
                }
                
                try {
                    this.log('开始建立WebRTC连接...', 'info');
                    await this.createPeerConnection();
                    await this.createOffer();
                    this.isCallActive = true;
                    this.updateButtonStates();
                } catch (error) {
                    this.log(`建立连接失败: ${error.message}`, 'error');
                }
            }
            
            async createPeerConnection() {
                const configuration = {
                    iceServers: [
                        { urls: 'stun:stun.l.google.com:19302' },
                        { urls: 'stun:stun1.l.google.com:19302' }
                    ]
                };
                
                this.peerConnection = new RTCPeerConnection(configuration);
                
                // 创建数据通道
                this.dataChannel = this.peerConnection.createDataChannel('messages', {
                    ordered: true
                });
                
                this.dataChannel.onopen = () => {
                    this.log('数据通道已打开', 'success');
                    this.updateButtonStates();
                };
                
                this.dataChannel.onmessage = (event) => {
                    this.log(`收到消息: ${event.data}`, 'success');
                };
                
                this.dataChannel.onclose = () => {
                    this.log('数据通道已关闭', 'warning');
                    this.updateButtonStates();
                };
                
                this.dataChannel.onerror = (error) => {
                    this.log(`数据通道错误: ${error}`, 'error');
                };
                
                // 处理接收到的数据通道
                this.peerConnection.ondatachannel = (event) => {
                    const channel = event.channel;
                    channel.onmessage = (event) => {
                        this.log(`收到消息: ${event.data}`, 'success');
                    };
                };
                
                // 处理远程视频流
                this.peerConnection.ontrack = (event) => {
                    this.log(`收到远程视频流: ${event.streams.length}个流, ${event.track.kind}轨道`, 'success');
                    if (event.streams && event.streams.length > 0) {
                        this.elements.remoteVideo.srcObject = event.streams[0];
                        this.log(`视频元素srcObject已设置`, 'info');
                        
                        // 添加视频元素事件监听
                        this.elements.remoteVideo.onloadedmetadata = () => {
                            this.log(`视频元数据已加载: ${this.elements.remoteVideo.videoWidth}x${this.elements.remoteVideo.videoHeight}`, 'success');
                        };
                        
                        this.elements.remoteVideo.onplay = () => {
                            this.log('视频开始播放', 'success');
                        };
                        
                        this.elements.remoteVideo.onerror = (error) => {
                            this.log(`视频播放错误: ${error}`, 'error');
                        };
                    } else {
                        this.log('没有接收到视频流', 'warning');
                    }
                };
                
                // 处理ICE候选
                this.peerConnection.onicecandidate = (event) => {
                    if (event.candidate) {
                        this.sendSignalingMessage({
                            type: 'ice-candidate',
                            candidate: event.candidate
                        });
                    }
                };
                
                // 连接状态变化
                this.peerConnection.onconnectionstatechange = () => {
                    const state = this.peerConnection.connectionState;
                    this.log(`WebRTC连接状态: ${state}`, 'info');
                    
                    if (state === 'connected') {
                        this.log('WebRTC P2P连接建立成功', 'success');
                    } else if (state === 'disconnected' || state === 'failed') {
                        this.log('WebRTC连接断开，尝试重新连接...', 'warning');
                        this.reconnectWebRTC();
                    }
                };
            }
            
            async createOffer() {
                this.log('=== 开始创建offer v2.0 ===', 'info');
                
                try {
                    // 方法1: 先创建一个虚拟的视频流
                    const canvas = document.createElement('canvas');
                    canvas.width = 640;
                    canvas.height = 480;
                    const ctx = canvas.getContext('2d');
                    ctx.fillStyle = 'black';
                    ctx.fillRect(0, 0, 640, 480);
                    
                    const stream = canvas.captureStream(1);
                    const videoTrack = stream.getVideoTracks()[0];
                    
                    // 添加视频轨道到PeerConnection
                    const sender = this.peerConnection.addTrack(videoTrack, stream);
                    this.log(`添加虚拟视频轨道: ${videoTrack.kind}`, 'info');
                    
                    // 立即移除轨道，但保留transceiver用于接收
                    this.peerConnection.removeTrack(sender);
                    
                    // 设置transceiver方向为recvonly
                    const transceivers = this.peerConnection.getTransceivers();
                    transceivers.forEach((t, i) => {
                        if (t.receiver.track && t.receiver.track.kind === 'video') {
                            t.direction = 'recvonly';
                            this.log(`设置视频transceiver ${i}方向为: ${t.direction}`, 'info');
                        }
                    });
                    
                    this.log(`当前transceivers数量: ${transceivers.length}`, 'info');
                    
                    // 创建offer
                    const offer = await this.peerConnection.createOffer();
                    await this.peerConnection.setLocalDescription(offer);
                    
                    this.log(`创建offer成功: type=${offer.type}, sdp长度=${offer.sdp.length}`, 'info');
                    
                    // 检查SDP是否包含视频
                    const hasVideo = offer.sdp.includes('m=video');
                    this.log(`offer是否包含视频: ${hasVideo}`, hasVideo ? 'success' : 'error');
                    
                    if (hasVideo) {
                        this.log('✅ offer包含视频媒体描述', 'success');
                    } else {
                        this.log('❌ offer不包含视频媒体描述', 'error');
                        this.log(`完整SDP: ${offer.sdp}`, 'error');
                    }
                    
                    this.sendSignalingMessage({
                        type: 'offer',
                        offer: offer
                    });
                    
                    this.log('发送Offer', 'info');
                    
                } catch (error) {
                    this.log(`创建offer失败: ${error.message}`, 'error');
                    console.error('createOffer error:', error);
                }
            }
            
            async handleSignalingMessage(data) {
                try {
                    if (data.type === 'answer') {
                        this.log('收到Answer', 'info');
                        await this.peerConnection.setRemoteDescription(data.answer);
                    } else if (data.type === 'ice-candidate') {
                        this.log('收到ICE候选', 'info');
                        await this.peerConnection.addIceCandidate(data.candidate);
                    }
                } catch (error) {
                    this.log(`处理信令消息错误: ${error.message}`, 'error');
                }
            }
            
            sendSignalingMessage(message) {
                if (this.socket && this.socket.connected) {
                    this.socket.emit('webrtc_signal', message);
                } else {
                    this.log('无法发送信令消息：未连接到服务器', 'error');
                }
            }
            
            sendMessage() {
                const message = this.elements.messageInput.value.trim();
                if (!message) return;
                
                if (this.dataChannel && this.dataChannel.readyState === 'open') {
                    this.dataChannel.send(message);
                    this.log(`发送消息: ${message}`, 'info');
                    this.elements.messageInput.value = '';
                } else {
                    this.log('数据通道未打开，无法发送消息', 'error');
                }
            }
            
            endCall() {
                if (this.peerConnection) {
                    this.peerConnection.close();
                    this.peerConnection = null;
                }
                
                if (this.dataChannel) {
                    this.dataChannel.close();
                    this.dataChannel = null;
                }
                
                this.elements.remoteVideo.srcObject = null;
                this.isCallActive = false;
                this.updateButtonStates();
                this.log('通话已结束', 'info');
            }
            
            refreshServers() {
                if (this.socket && this.socket.connected) {
                    this.socket.emit('get_servers');
                    this.log('刷新服务器列表...', 'info');
                } else {
                    this.log('请先连接到信令服务器', 'error');
                }
            }
            
            async reconnectWebRTC() {
                if (this.isCallActive) {
                    this.log('尝试重新建立WebRTC连接...', 'warning');
                    this.endCall();
                    
                    setTimeout(() => {
                        this.startCall();
                    }, 3000);
                }
            }
            
            updateStatus(type, message) {
                this.elements.connectionStatus.className = `status ${type}`;
                this.elements.connectionStatus.textContent = `状态: ${message}`;
            }
            
            updateButtonStates() {
                this.elements.connectBtn.disabled = this.isConnected;
                this.elements.startCallBtn.disabled = !this.isConnected || this.isCallActive;
                this.elements.endCallBtn.disabled = !this.isCallActive;
                this.elements.refreshServersBtn.disabled = !this.isConnected;
                
                const dataChannelOpen = this.dataChannel && this.dataChannel.readyState === 'open';
                this.elements.messageInput.disabled = !dataChannelOpen;
                this.elements.sendMessageBtn.disabled = !dataChannelOpen;
            }
            
            log(message, type = 'info') {
                const timestamp = new Date().toLocaleTimeString();
                const logEntry = document.createElement('div');
                logEntry.className = `log-entry ${type}`;
                logEntry.textContent = `[${timestamp}] ${message}`;
                
                this.elements.messageLog.appendChild(logEntry);
                this.elements.messageLog.scrollTop = this.elements.messageLog.scrollHeight;
                
                console.log(`[${type.toUpperCase()}] ${message}`);
            }
        }
        
        // 初始化WebRTC客户端
        const client = new WebRTCClient();
        
        // 页面加载完成后自动连接
        window.addEventListener('load', () => {
            client.log('页面加载完成', 'info');
            // 自动连接到信令服务器
            setTimeout(() => {
                client.connect();
                // 连接成功后自动开始通话
                setTimeout(() => {
                    if (client.isConnected) {
                        client.startCall();
                    }
                }, 2000);
            }, 1000);
        });
    </script>
</body>
</html>